package kingwing.dc.core.view.rule;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.annotation.Resource;

import kingwing.dc.core.model.Infos;
import kingwing.dc.core.model.MyUrls;
import kingwing.dc.core.model.Rule;
import kingwing.dc.core.model.RuleDetail;
import kingwing.dc.core.orm.hibernate.CommonHibernateDao;
import kingwing.dc.core.sevice.HTMLProcessor;
import kingwing.dc.core.sevice.JSProcessor;
import kingwing.dc.core.sevice.SinglePageHtmlProcessor;
import kingwing.dc.core.view.frame.main.CrawlerMainFramePR;
import kingwing.dc.core.view.info.InfoShowPR;

import org.apache.commons.lang.StringUtils;
import org.hibernate.Session;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Disjunction;
import org.hibernate.criterion.Restrictions;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import us.codecraft.webmagic.ResultItems;
import us.codecraft.webmagic.Site;
import us.codecraft.webmagic.Spider;
import us.codecraft.webmagic.pipeline.ResultItemsCollectorPipeline;

import com.bstek.bdf2.core.business.IUser;
import com.bstek.bdf2.core.context.ContextHolder;
import com.bstek.bdf2.core.model.Url;
import com.bstek.bdf2.core.orm.hibernate.ISessionCallback;
import com.bstek.dorado.annotation.DataProvider;
import com.bstek.dorado.annotation.DataResolver;
import com.bstek.dorado.annotation.Expose;
import com.bstek.dorado.data.entity.EntityState;
import com.bstek.dorado.data.entity.EntityUtils;
import com.bstek.dorado.data.provider.Criteria;
import com.bstek.dorado.data.provider.Criterion;
import com.bstek.dorado.data.provider.Page;
import com.bstek.dorado.data.provider.filter.SingleValueFilterCriterion;
import com.google.gson.JsonObject;

@Component
public class RulePR {

	@Resource(name=CommonHibernateDao.BEAN_ID)
	private CommonHibernateDao commonHibernateDao;
	@Resource(name="infoShowPR")
	private InfoShowPR infoShowPR;
	@Resource(name="crawlerMainFramePR")
	private CrawlerMainFramePR crawlerMainFramePR;
	
	/**
	 * 根据规则ID分页获取规则明细
	 * @param pages
	 * @param id
	 */
	@DataProvider
	public void getRuleDetailforPages(Page<RuleDetail> pages,String id){
		DetachedCriteria detachedCriteria = DetachedCriteria.forClass(RuleDetail.class);
		if (StringUtils.isNotBlank("id")) {
			detachedCriteria.add(Restrictions.eq("ruleId", id));
		}
		detachedCriteria.add(Restrictions.eq("createUserId", ContextHolder.getLoginUserName()));
		this.commonHibernateDao.pagingQuery(pages, detachedCriteria);
	}
	
	/**
	 * 分页获取所有的规则
	 * @return
	 */
	@DataProvider
	public void  getRulePage(Page<Rule> page,Criteria criteria){
		DetachedCriteria detachedCriteria = this.commonHibernateDao.buildDetachedCriteria(criteria, Rule.class);
		detachedCriteria.add(Restrictions.eq("createUserId", ContextHolder.getLoginUserName()));
		this.commonHibernateDao.pagingQuery(page, detachedCriteria);
	}
	/**
	 * 获取所有的规则
	 * @return
	 */
	@SuppressWarnings("unchecked")
	@DataProvider
	public Collection<Rule>  getRule(){
		DetachedCriteria detachedCriteria = DetachedCriteria.forClass(Rule.class);
		detachedCriteria.add(Restrictions.eq("createUserId", ContextHolder.getLoginUserName()));
		return (Collection<Rule>) this.commonHibernateDao.query(detachedCriteria);
	}
	
	/**
	 * 增删改规则及规则明细
	 * @param rules
	 */
	@Transactional
	@DataResolver
	public void saveRule(final Collection<Rule> rules){
		
		for (Rule rule : rules) {
			EntityState state = EntityUtils.getState(rule);
			if (state.equals(EntityState.NEW)) {
				this.commonHibernateDao.save(rule);
				operationRuleDetail(rule);
			}else if (state.equals(EntityState.MODIFIED)) {
				this.commonHibernateDao.update(rule);
				operationRuleDetail(rule);
			}else if (state.equals(EntityState.DELETED)) {
				Map<String, Object> map = new HashMap<String, Object>();
				map.put("ruleId", rule.getId());
				List<RuleDetail> ruleDetails = this.commonHibernateDao.query("from "+RuleDetail.class.getName()+" where ruleId=:ruleId",map);
				for (RuleDetail ruleDetail : ruleDetails) {
					this.commonHibernateDao.delete(ruleDetail);
				}
				
				infoShowPR.deleteAllInfos(rule.getRuleName(),rule.getCreateUserId());
				crawlerMainFramePR.deleteMysql(rule.getRuleName(),rule.getCreateUserId());
				
				
				this.commonHibernateDao.delete(rule);
				
			}else if (state.equals(EntityState.NONE)) {
				operationRuleDetail(rule);
			}
		}
	}

	/**
	 * 增删改规则明细
	 * @param rule
	 */
	@Transactional
	private void operationRuleDetail(Rule rule) {
		Collection<RuleDetail> ruleDetails = rule.getRuleDetails();
		if (ruleDetails!=null) {
			for (RuleDetail ruleDetail : ruleDetails) {
				EntityState stateDetail = EntityUtils.getState(ruleDetail);
				if (stateDetail.equals(EntityState.NEW)) {
					ruleDetail.setRuleId(rule.getId());
					this.commonHibernateDao.save(ruleDetail);
				}else if (stateDetail.equals(EntityState.MODIFIED)) {
					this.commonHibernateDao.update(ruleDetail);
				}else if (stateDetail.equals(EntityState.DELETED)) {
					this.commonHibernateDao.delete(ruleDetail);
				}
			}
		}
	}
	
	/**
	 * 生成站点列表菜单
	 * @param name
	 * @param url
	 * @param icon
	 * @param parentId
	 */
	@Transactional
	@Expose
	public void generatedMenu(Map<String, Object> params){
		
		MyUrls myUrls = new MyUrls();
		if (!params.isEmpty() && params != null) {
			String parentId = params.get("parentId").toString();
			String name = params.get("name").toString();
			String url = params.get("url").toString();
			String icon = params.get("icon").toString();
			String username = params.get("username").toString();
			
			if (StringUtils.isBlank(name)) {
				return;
			}
			name = name.substring(0, name.length()-1);
			String[] names = name.split(","); 
			
			for (String item : names) {
				
				if (isExistsUrl(item)) {
					deleteUrl(item);
				}else{
					myUrls.setParentId(parentId);
					myUrls.setName(item);
					myUrls.setUrl(url);
					myUrls.setIcon(icon);
					myUrls.setCompanyId(username);
					this.commonHibernateDao.save(myUrls);
				}
				
			}
			
			
		}
		
		
	}

	/**
	 * 根据菜单名称删除菜单
	 * @param name
	 */
	@Transactional
	private void deleteUrl(final String name) {
		
		if (StringUtils.isNotBlank(name)) {
			this.commonHibernateDao.doInHibernateSession(new ISessionCallback<Object>() {
				
				@Override
				public Object doInSession(Session session) {
					String username = ContextHolder.getLoginUserName();
					session.createQuery("delete MyUrls where name=:name and createUserId=:username")
					.setString("name", name)
					.setString("username", username)
					.executeUpdate();
					return null;
				}
			});
		}
	}

	/**
	 * 根据菜单名称获取判断菜单是否存在
	 * @param name
	 * @return
	 */
	private boolean isExistsUrl(String name) {
		Map<String, Object> map = new HashMap<>();
		String username = ContextHolder.getLoginUserName();
		String hql = "select count(u) from "+MyUrls.class.getName()+" u";
		if (StringUtils.isNotBlank(name)) {
			map.put("urlName", name);
			map.put("username", username);
			hql+=" where u.name=:urlName and u.createUserId=:username";
		}
		int count = this.commonHibernateDao.queryForInt(hql, map);
		if (count>0) {
			return true;
		}else {
			return false;
		}
	}
	
	/**
	 * 开始采集数据，进行封装
	 * @param params
	 * @return
	 */
	@SuppressWarnings({ "unchecked" })
	@Transactional
	@Expose
	public String beginDataCollection(Map<String, Object> params){
		
		//获取规则集合
		List<Rule> rules = new ArrayList<>();
		String keyword = "";
		int pages = 0;
		if (params != null && !params.isEmpty()) {
			if (params.get("entity") instanceof Rule) {
				rules.add((Rule) params.get("entity"));
			}else {
				rules =  (List<Rule>) params.get("entity");
			}
			keyword = params.get("keyword").toString();
			String page = params.get("pages").toString();
			if (StringUtils.isNotBlank(page)) {
				pages = Integer.valueOf(page);
			}
		}
		if (rules == null) {
			return "信息采集失败！请检查是否存在要采集站点！";
		}
		
		for (Rule rule : rules) {
			if(rule.getId()==null){
				continue;
			}
			//格式化URL
			String[] Urls = scanUrlsHandle(rule,keyword,pages);
			
			Site site = Site.me().setDomain(rule.getDomains()).setSleepTime(3000).setRetryTimes(3);
			
			List<RuleDetail> ruleDetails = getRuleDetails(rule.getId());
			
			ResultItemsCollectorPipeline resultItemsCollectorPipeline = new ResultItemsCollectorPipeline();
			
			if (rule.getGrabType()==0) {
				//根据页面类型选择不同的采集器
				if (rule.getPageType()==0) {
					Spider s = Spider.create(new HTMLProcessor(site, rule, ruleDetails));
					s.addUrl(Urls);
					s.thread(10);
					s.addPipeline(resultItemsCollectorPipeline);
					s.run();
				}else {
					Spider s = Spider.create(new JSProcessor(site, ruleDetails,rule));
					s.addUrl(Urls);
					s.thread(10);
					s.addPipeline(resultItemsCollectorPipeline);
					s.run();
				}
			}else{
				Spider s = Spider.create(new SinglePageHtmlProcessor(site, rule, ruleDetails));
				s.addUrl(Urls);
				s.thread(10);
				s.addPipeline(resultItemsCollectorPipeline);
				s.run();
			}
			
			//获取采集结果，进行数据持久化
			List<ResultItems> list = resultItemsCollectorPipeline.getCollected();
			for (ResultItems resultItems : list) {
				if (resultItems.get("infos") instanceof List) {
					List<Infos> infosList = resultItems.get("infos");
					for (Infos infos : infosList) {
						if (infos!=null && !isRepetitionInfos(infos.getField1())) {
							this.commonHibernateDao.save(infos);
						}
					}
				}else {
					final Infos infos = resultItems.get("infos");
					if (infos!=null && !isRepetitionInfos(infos.getField1())) {
						this.commonHibernateDao.save(infos);
					}
				}
			}
			
		}
		
		return "采集完成！";
	}

	/**
	 * 根据规则ID获取规则明细
	 * @param ruleId
	 * @return
	 */
	public List<RuleDetail> getRuleDetails(String ruleId) {
		Map<String, Object> map = new HashMap<>();
		String hql = "from "+RuleDetail.class.getName()+" re";
		if (StringUtils.isNotBlank(ruleId)) {
			map.put("ruleId", ruleId);
			hql+=" where re.ruleId=:ruleId";
		}
		List<RuleDetail> ruleDetails = this.commonHibernateDao.query(hql, map);
		return ruleDetails;
	}

	/**
	 * 入口URL自定义处理
	 * @param scanUrls
	 * @param helperUrlRegexes
	 * @return
	 */
	public String[] scanUrlsHandle(Rule rule,String keyword,int pages) {
		
		String scanUrls = rule.getScanUrls();
		List<String> url = new ArrayList<>();
		
		String[] scan = scanUrls.split(",");
		
		int count=1;	//获取url中默认的页数
		String pageKey = "";
		String keyWordKey = "";
		
		for (String surl : scan) {
			
			//使用正则提取url中的自定义规则符号
			String reg = "\\[\\w+=?\\d*[\\u4e00-\\u9fa5]*\\]";
			Pattern pattern = Pattern.compile (reg);
			Matcher matcher = pattern.matcher (surl);
			List<String> keys = new ArrayList<>();
			while (matcher.find ()) {
				//System.out.println(matcher.group ());
				keys.add(matcher.group ());
			}
			
			for (String key : keys) {
				String urlDetail = key.substring(key.indexOf("[")+1, key.indexOf("]"));
				
				if (urlDetail.startsWith("page")) {
					pageKey = key;
					if (urlDetail.matches("page=?\\d*")) {
						if (count>0) {
							String[] numbers = urlDetail.split("=");
							if (numbers.length>1) {
								count = Integer.parseInt(numbers[1]);
							}
						}
					}
				}
				
				if (urlDetail.startsWith("keyword")) {
					keyWordKey = key;
					if (urlDetail.matches("keyword=?[\\u4e00-\\u9fa5]*")) {
						if (StringUtils.isBlank(keyword)) {
							String[] keywords = urlDetail.split("=");
							if (keywords.length>1) {
								keyword = keywords[1];
							}
						}
					}
				}
				
			}
			
			//根据不同类型的自定义表达式调整原url
			if (StringUtils.isNotBlank(pageKey) && StringUtils.isNotBlank(keyWordKey)) {
				
				if (pages>0) {
					for (int i = 0; i <= pages; i++) {
						String newsurl = surl.replace(pageKey, i+"");
						newsurl=newsurl.replace(keyWordKey, keyword);
						url.add(newsurl);
					}
				}else {
					for (int i = 0; i <= count; i++) {
						String newsurl = surl.replace(pageKey, i+"");
						newsurl=newsurl.replace(keyWordKey, keyword);
						url.add(newsurl);
					}
				}
				
			}else if (StringUtils.isNotBlank(pageKey)) {
				if (pages>0) {
					for (int i = 0; i <= pages; i++) {
						String newsurl = surl.replace(pageKey, i+"");
						newsurl=newsurl.replace(keyWordKey, keyword);
						url.add(newsurl);
					}
				}else {
					for (int i = 0; i <= count; i++) {
						String newsurl = surl.replace(pageKey, i+"");
						newsurl=newsurl.replace(keyWordKey, keyword);
						url.add(newsurl);
					}
				}
			}else if(StringUtils.isNotBlank(keyWordKey)){
				String newsurl=surl.replace(keyWordKey, keyword);
				url.add(newsurl);
			}else {
				url.add(surl);
			}
			
		}
		
		
		//判断装链接的list里存不存在链接
		String[] Urls;
		if (url.size()>0) {
			Urls = url.toArray(new String[url.size()]);
		}else {
			url.add(scanUrls);
			Urls = url.toArray(new String[url.size()]);
		}
		return Urls;
	}
	
	/**
	 * 对采集的数据进行去重操作
	 * @param title
	 * @return
	 */
	public boolean isRepetitionInfos(String title){
		
		DetachedCriteria detachedCriteria = DetachedCriteria.forClass(Infos.class);
		
		int count = 0;
		if (StringUtils.isNotBlank(title)) {
			Disjunction disjunction = Restrictions.disjunction();
			
			disjunction.add(Restrictions.eq("field1", title));
			disjunction.add(Restrictions.eq("field2", title));
			disjunction.add(Restrictions.eq("field3", title));
			disjunction.add(Restrictions.eq("field4", title));
			disjunction.add(Restrictions.eq("field5", title));
			disjunction.add(Restrictions.eq("field6", title));
			disjunction.add(Restrictions.eq("field7", title));
			disjunction.add(Restrictions.eq("field8", title));
			
			detachedCriteria.add(disjunction);
			detachedCriteria.add(Restrictions.eq("createUserId", ContextHolder.getLoginUserName()));
			count = this.commonHibernateDao.queryCount(detachedCriteria);
		}
		
		if (count>0) {
			return true;
		}else {
			return false;
		}
		
	}
	/**
	 * 将站点批量分配给用户
	 * @param rules
	 * @param users
	 * @return
	 */
	@Transactional
	@SuppressWarnings("unchecked")
	@Expose
	public String rulesToUser(final Collection<Rule> rules,final Collection<IUser> users){
		
		
		/*return commonHibernateDao.doInHibernateSession(new ISessionCallback<String>() {
			@Override
			public String doInSession(Session session) {
				// TODO Auto-generated method stub
				return "分配成功！";
			}
		});*/
		for (IUser iUser : users) {
			generatedMenu(rules,iUser.getUsername());
			for (Rule rule : rules) {
				DetachedCriteria detachedCriteria = DetachedCriteria.forClass(RuleDetail.class);
				detachedCriteria.add(Restrictions.eq("ruleId", rule.getId()));
				Collection<RuleDetail> ruleDetails = (Collection<RuleDetail>) commonHibernateDao.query(detachedCriteria);
				
				if(!isExistRule(rule, iUser.getUsername())){
					rule.setCreateUserId(iUser.getUsername());
					rule.setJobTimeRuleID(null);
					String ruleId = saveRule(rule);
					if (ruleDetails!=null&&!ruleDetails.isEmpty()) {
						saveRuleDetail(ruleDetails,ruleId,iUser.getUsername());
					}
				}else{
//					updataRulesforUsers(rule, iUser.getUsername());
					return rule.getRuleName()+"站点已存在用户："+iUser.getUsername()+"中，不可以重复添加！";
				}
				
			}
		}
		return "分配成功！";
	}
	/**
	 * 保存规则站点并且返回保存后的站点ID
	 * @param rule
	 * @return
	 */
	private String saveRule(final Rule rule){
		return this.commonHibernateDao.doInHibernateSession(new ISessionCallback<String>() {

			@Override
			public String doInSession(Session session) {
				// TODO Auto-generated method stub
				session.save(rule);
				return rule.getId();
			}
		});
	}
	
	private void saveRuleDetail(final Collection<RuleDetail> ruleDetails,final String ruleId,final String username){
		if (ruleDetails!=null&&ruleDetails.size()!=0) {
			this.commonHibernateDao.doInHibernateSession(new ISessionCallback<Object>() {

				@Override
				public Object doInSession(Session session) {
					// TODO Auto-generated method stub
					for (RuleDetail ruleDetail : ruleDetails) {
						ruleDetail.setCreateUserId(username);
						ruleDetail.setRuleId(ruleId);
						session.save(ruleDetail);
					}
					return null;
				}
			});
		}
	}
	
	/**
	 * 判断当前用户下是否有相同的站点
	 * @param rule
	 * @param createUserId
	 * @return
	 */
	private boolean isExistRule(Rule rule,String createUserId){
		
		DetachedCriteria detachedCriteria = DetachedCriteria.forClass(Rule.class);
		
		detachedCriteria.add(Restrictions.and(Restrictions.eq("ruleName", rule.getRuleName()), 
				Restrictions.eq("createUserId", createUserId)));
		
		int count = this.commonHibernateDao.queryCount(detachedCriteria);
		
		if (count>0) {
			return true;
		}else{
			return false;
		}
	}
	/**
	 * 生成导航
	 * @param rules
	 * @param username
	 */
	private void generatedMenu(Collection<Rule> rules,String username){
		DetachedCriteria detachedCriteria = DetachedCriteria.forClass(MyUrls.class);
		detachedCriteria.add(Restrictions.eq("name", "站点列表"));
		List<MyUrls> myUrls = (List<MyUrls>) this.commonHibernateDao.query(detachedCriteria);
		String parentID = null;
		if (myUrls!=null&&myUrls.size()!=0) {
			parentID = myUrls.get(0).getId();
		}
		for (Rule rule : rules) {
			if (isExistsUrl(rule.getRuleName(),username)) {
				deleteUrl(rule.getRuleName(),username);
			}else{
				MyUrls myUrl = new MyUrls();
				myUrl.setParentId(parentID);
				myUrl.setName(rule.getRuleName());
				myUrl.setUrl("kingwing.dc.core.view.info.InfoShow.d");
				myUrl.setIcon("url(>skin>common/icons.gif) -80px -180px");
				myUrl.setCompanyId(username);
				myUrl.setCreateUserId(username);
				this.commonHibernateDao.saveNotUser(myUrl);
			}
		}
		
	}
	
	/**
	 * 根据菜单名称获取判断菜单是否存在
	 * @param name
	 * @return
	 */
	private boolean isExistsUrl(String name,String username) {
		Map<String, Object> map = new HashMap<>();
		String hql = "select count(u) from "+MyUrls.class.getName()+" u";
		if (StringUtils.isNotBlank(name)) {
			map.put("urlName", name);
			map.put("username", username);
			hql+=" where u.name=:urlName and u.createUserId=:username";
		}
		int count = this.commonHibernateDao.queryForInt(hql, map);
		if (count>0) {
			return true;
		}else {
			return false;
		}
	}
	
	/**
	 * 根据菜单名称删除菜单
	 * @param name
	 */
	@Transactional
	private void deleteUrl(final String name,final String username) {
		
		if (StringUtils.isNotBlank(name)) {
			this.commonHibernateDao.doInHibernateSession(new ISessionCallback<Object>() {
				
				@Override
				public Object doInSession(Session session) {
					session.createQuery("delete MyUrls where name=:name and createUserId=:username")
					.setString("name", name)
					.setString("username", username)
					.executeUpdate();
					return null;
				}
			});
		}
	}
	
	/**
	 * 更新当前用户下的站点
	 * @param rule
	 * @param createUserId
	 */
	/*
	@SuppressWarnings({ "unchecked" })
	private void updataRulesforUsers(final Rule rule,final String createUserId){
		
		DetachedCriteria detachedCriteriaRule = DetachedCriteria.forClass(Rule.class);
		DetachedCriteria detachedCriteriaUpdateRuleDetail = DetachedCriteria.forClass(RuleDetail.class);
		DetachedCriteria detachedCriteriaOldRuleDetail = DetachedCriteria.forClass(RuleDetail.class);
		
		detachedCriteriaRule.add(Restrictions.and(Restrictions.eq("ruleName", rule.getRuleName()), 
				Restrictions.eq("createUserId", createUserId)));
		detachedCriteriaOldRuleDetail.add(Restrictions.and(Restrictions.eq("ruleId", rule.getId()), 
				Restrictions.eq("createUserId", rule.getCreateUserId())));
		
		
		final List<Rule> rules = (List<Rule>) this.commonHibernateDao.query(detachedCriteriaRule);
		Rule updataRule = null;
		if (!rules.isEmpty()&&rules.size()>0) {
			updataRule = rules.get(0);
		}
		
		detachedCriteriaUpdateRuleDetail.add(Restrictions.and(Restrictions.eq("ruleId", updataRule.getId()), 
				Restrictions.eq("createUserId", createUserId)));
		
		
		List<RuleDetail> updataRuleDetails = (List<RuleDetail>) this.commonHibernateDao.query(detachedCriteriaUpdateRuleDetail);
		final List<RuleDetail> oldRuleDetails = (List<RuleDetail>) this.commonHibernateDao.query(detachedCriteriaOldRuleDetail);
		
		for (RuleDetail ruleDetail : updataRuleDetails) {
			this.commonHibernateDao.delete(ruleDetail);
		}
		
		
		this.commonHibernateDao.doInHibernateSession(new ISessionCallback<Object>() {
			
			@Override
			public Object doInSession(Session session) {
				// TODO Auto-generated method stub
				Rule updataRule = null;
				if (!rules.isEmpty()&&rules.size()>0) {
					updataRule = rules.get(0);
				}
				if (updataRule!=null) {
					updataRule.setDomains(rule.getDomains());
					updataRule.setScanUrls(rule.getScanUrls());
					updataRule.setContentUrls(rule.getContentUrls());
					updataRule.setContentId(rule.getContentId());
					updataRule.setContentUrlRegexes(rule.getContentUrlRegexes());
					updataRule.setHelperUrlRegexes(rule.getHelperUrlRegexes());
					updataRule.setPageType(rule.getPageType());
					updataRule.setGrabType(rule.getGrabType());
					updataRule.setCheck(rule.isCheck());
					updataRule.setRemark(rule.getRemark());
					session.update(updataRule);
					for (RuleDetail ruleDetail : oldRuleDetails) {
						ruleDetail.setCreateUserId(createUserId);
						ruleDetail.setRuleId(updataRule.getId());
						session.save(ruleDetail);
					}
				}
				return null;
			}
		});
		
	}
	*/
}
